package com.zzy.service.impl;

import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson.JSONObject;
import com.zzy.model.UserModel;
import com.zzy.util.MoveNodeCmd;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import com.zzy.service.ActivityConsumerService;
import com.zzy.util.UtilDiagrams;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class ActivityConsumerServiceImpl implements ActivityConsumerService {

    @Resource
    private ProcessEngine processEngine;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private HistoryService historyService;

    @Resource
    private ManagementService managementService;

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public String  getVersion(){
        return ProcessEngine.VERSION;
    }



    @Override
    public List getFlowBpmnList() {
        List list = new ArrayList();
        String deployList[] = UtilDiagrams.list();
        if(deployList.length>0){
            for(String dep : deployList){
                Map map = new HashMap();
                String name = dep.substring(0  , dep.lastIndexOf(".") );
                map.put("name",name);
                map.put("type","bpmn");//因为都是 bpmn 文件
                map.put("fullName",dep);
                list.add(map);
            }
        }
        return list;
    }

    /**
     * 部署流程
     * 部署完流程，会在这几个表中生成数据
     * act_re_deployment
     * act_ge_bytearray
     * act_re_procdef
     * */
    public Integer deployFlow(String processName){
        //要判断，是否已经部署，如果已经部署， 部署的流程是否已经结束， 删除还是保留，都是问题
        RepositoryService service = processEngine.getRepositoryService();
        DeploymentBuilder build= service.createDeployment();
        if (null != processName) {
            // 直接部署  注意路径
            build.addClasspathResource("processes/"+processName);
            build.deploy();
           /* if(processName.equals("Bxsp.bpmn")){
                service.createDeployment().addClasspathResource("diagrams/" + processName)
                        .addClasspathResource("form/bxsp.form")
                        .deploy();
            }else if(processName.equals("Hello.bpmn")){//请假表单
                service.createDeployment().addClasspathResource("diagrams/" + processName)
                        .addClasspathResource("form/qingjia.form")
                        .addClasspathResource("form/qingjia_audit.form")
                        .deploy();
            }else{
                service.createDeployment().addClasspathResource("diagrams/" + processName).deploy();
            }*/
        }
        return 0;
    }

    /**取消部署流程*/
    public String cancalDeployFlow(String processDefinitionId){
        //判断是否 流程 已经执行
        RepositoryService service = processEngine.getRepositoryService();
        String state = "";
        try {
            //根据 流程 ID 获取当前流程
            ProcessDefinition processDefinition = service.createProcessDefinitionQuery()
                    .processDefinitionId(processDefinitionId).singleResult();
            //查询该流程是否已经，启动
            service.deleteDeployment(processDefinition.getDeploymentId(),true);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return state;
    }


    /** 已经 部署的 流程   */
    public List<Map<String,Object>> getDeployedList(){
        List<Map<String,Object>> list = new LinkedList<>();
        List<ProcessDefinition> plist = repositoryService.createProcessDefinitionQuery().list();
        //获取部署时间等信息
        List<Deployment> dlist = repositoryService.createDeploymentQuery().list();
        plist.forEach(  p  -> {
            Map<String,Object> map = new HashMap<>();
            map.put("id",p.getId());
            map.put("category",p.getCategory());
            map.put("name",p.getName());
            map.put("key",p.getKey());
            map.put("description",p.getDescription());
            map.put("version",p.getVersion());
            map.put("resourceName",p.getResourceName());
            map.put("deploymentId",p.getDeploymentId());
            map.put("diagramResourceName",p.getDiagramResourceName());
            map.put("hasStartFormKey",p.hasStartFormKey());
            map.put("hasGraphicalNotation",p.hasGraphicalNotation());
            map.put("isSuspended",p.isSuspended());
            map.put("tenantId",p.getTenantId());
            map.put("engineVersion",p.getEngineVersion());
            for(Deployment d : dlist ){
                if(d.getId().equals(p.getDeploymentId())){
                    map.put("craeteTime",sdf.format(d.getDeploymentTime()));
                }
            }
            list.add(map);
        });

        return list;
    }

    @Override
    /** 发起流程 */
    public Map<String,Object>  startFlow(Map<String,String> param){
        //首先获取流程定义ID
        String processDefinitionId = param.get("processDefinitionId");
        String userId = param.get("userId");
        //SELECT * from  act_hi_procinst; 设置流程 发起记录表的，发起人字段 START_USER_ID_
        Authentication.setAuthenticatedUserId(userId);//
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId).singleResult();
        //发起流程的时候，可以给流程 设置 bussinessKey ，也可以不设置,这里设置为 流程定义的key
        String bussinessKey = processDefinition.getKey();
        ProcessInstance p = runtimeService.startProcessInstanceById(processDefinitionId,bussinessKey);
        //如果需要在第一节点设置参数则可以传递参数
        // SELECT * from act_hi_varinst;
        // SELECT * from act_ru_variable;
//        Map<String,Object> paramMap = new HashMap<>();
//        paramMap.put("nextTaskUser","nextTaskUser");
//        ProcessInstance p = runtimeService.startProcessInstanceById(processDefinitionId,bussinessKey,paramMap);
        Map<String,Object> res = new HashMap<>();
        res.put("id",p.getId());
        res.put("name",p.getName());
        res.put("businessKey",p.getBusinessKey());
        res.put("deploymentId",p.getDeploymentId());
        res.put("description",p.getDescription());
        res.put("processDefinitionId",p.getProcessDefinitionId());
        res.put("processDefinitionKey",p.getProcessDefinitionKey());
        res.put("tenantId",p.getTenantId());//多系统共用数据库，可以用 tenantId 区分
        res.put("startUserId",p.getStartUserId());
        res.put("startTime",p.getStartTime());
        res.put("processDefinitionVersion",p.getProcessDefinitionVersion());
        res.put("processVariables",p.getProcessVariables());
        return res;
    }

    /**获取 流程的所有节点 */
    public Map<String,Object> getAllNode(String processDefinitionId) {
        Map<String,Object> map = new HashMap<>();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        Process process = bpmnModel.getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        List<UserTask> list = process.findFlowElementsOfType(UserTask.class);
        map.put("allNode",flowElements);
        map.put("taskNode",list);
        return  map;
    }
    /**获取 流程的所有节点 isTask 是否是任务节点  */
    public List<String> getAllNode(String processInstanceId,boolean isTask) {
        List<String> list = new ArrayList<>();
        ProcessInstance p = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(p.getProcessDefinitionId());
        Process process = bpmnModel.getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        List<UserTask> taskList = process.findFlowElementsOfType(UserTask.class);
        if(isTask){//只获取流程节点
            for(UserTask t : taskList){
                list.add(t.getId());
            }
        }else{//所有节点
            for(FlowElement f : flowElements){
                list.add(f.getId());
            }
        }
        return  list;
    }

    /** 加载 由 某人 发起的流程 */
    public List<Map<String,Object>> getStartedTaskByUserid(String userId) {
        List<Map<String,Object>> list = new ArrayList<>();
        List<HistoricProcessInstance> hpiList = new ArrayList<>();
        if(userId.equals("zzy")){//管理员
            hpiList = historyService.createHistoricProcessInstanceQuery().list();
        }else {
            hpiList = historyService.createHistoricProcessInstanceQuery().startedBy(userId).list();
        }
        for( HistoricProcessInstance p :  hpiList ){
            Map<String,Object> res = new HashMap<>();
            res.put("id",p.getId());
            res.put("name",p.getName());
            res.put("businessKey",p.getBusinessKey());
            res.put("deploymentId",p.getDeploymentId());
            res.put("description",p.getDescription());
            res.put("processDefinitionId",p.getProcessDefinitionId());
            res.put("processDefinitionKey",p.getProcessDefinitionKey());
            res.put("tenantId",p.getTenantId());//多系统共用数据库，可以用 tenantId 区分
            res.put("startUserId",p.getStartUserId());
            res.put("startTime",p.getStartTime());
            res.put("processDefinitionVersion",p.getProcessDefinitionVersion());
            res.put("processVariables",p.getProcessVariables());
            list.add(res);
        }
        return list;
    }


    @Override
    /** 取消发起流程[删除已经发起的流程]*/
    public void cancalStartedFlow(String processInstanceId,String userId) {
        //判断是否 有task 这里不判断
        runtimeService.deleteProcessInstance(processInstanceId,userId+":删除了流程");
    }



    @Override
    /** 获取待处理的流程 [获取代办] */
    public List<Map<String,Object>>  getFlowTask(String userId) {
        List<Map<String,Object>> list = new ArrayList<>();
        TaskQuery taskQuery = taskService.createTaskQuery();
        List<Task> tasks = new ArrayList<>();
        if(userId.equals("zzy")){//管理员
            tasks = taskQuery.list();
        }else {
            tasks = taskQuery.taskAssignee(userId).list();
        }
        for(Task t : tasks){
            Map<String,Object> res = new HashMap<>();
            res.put("id",t.getId());
            res.put("name",t.getName());
            res.put("assignee",t.getAssignee());
            res.put("createTime",sdf.format(t.getCreateTime()));
            res.put("processInstanceId",t.getProcessInstanceId());
            res.put("processDefinitionId",t.getProcessDefinitionId());
            res.put("tenantId",t.getTenantId());//多系统共用数据库，可以用 tenantId 区分
            res.put("processVariables",t.getProcessVariables());
            list.add(res);
        }
        return list;
    }

    @Override
    /** 完成任务[完成代办]*/
    public String completeTask(Map<String,String> param) {
        String taskId = param.get("taskId");
        String userId = param.get("userId");
        // assignNode , assignUser这两个参数 做成 json 格式，测试 跨节点 指定处理人
        String assignNode  = param.get("assignNode");
        String assignUser  = param.get("assignUser");
        String approveOpinion = param.get("approveOpinion");//审核意见
        String auditOne  = param.get("auditOne");
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if(task==null){
            return "任务不存在";
        }
        ProcessInstance p = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
        String processInstanceId = p.getId();
        // 这里做了两个流程，
        Map<String,Object> variables = new HashMap<>();//完成任务的参数表
       if(p.getProcessDefinitionKey().equals("CollectiveExamination")){//会审的流程
           if(task.getTaskDefinitionKey().equals("T202")){//会审设置专家的节点
               // 流程图设置了 expert 和 ${expertList}
               List<String> expertList = new ArrayList<>();
               expertList.add("expert001");
               expertList.add("expert002");
               variables.put("expert",expertList);
           }
       }else if(p.getProcessDefinitionKey().equals("DevApproveProcess")){//第一节点指定第三节点的流程
           //流程第二节点有个，submitNum==1 的判断
           variables.put("submitNum",param.get("submitNum"));
           //指定第三节点处理人
           if(StringUtils.isNoneBlank(assignUser) && StringUtils.isNoneBlank(assignNode)){
               String var ="{\"assignUser\":\""+assignUser+"\",\"assignNode\":\""+assignNode+"\"}";//采用json存储，能放更多参数
               variables.put("assignee",var);//把 指定节点处理人 保存到参数表中   act_hi_varinst  act_ru_variable;
           }
       }

        if(auditOne.equals("y")){//是否是一票 通过
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
            for(Task t : tasks){
                taskService.addComment(t.getId(),processInstanceId,userId + "完成了任务:"+ approveOpinion);//审核意见
                taskService.complete(t.getId(),variables);
            }
        }else{
            taskService.addComment(taskId,processInstanceId,userId + "完成了任务:"+ approveOpinion);//审核意见
            taskService.complete(taskId,variables);
        }

        //完成任务成功后，获取当前task
        List<Task> nextTasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        if(nextTasks==null || nextTasks.size()==0){// 流程归档了

        }else if( nextTasks.size()==1){
            Task t = nextTasks.get(0);
            if(p.getProcessDefinitionKey().equals("DevApproveProcess")){//第一节点指定第三节点的流程
                // 假设这里处理 指定人
                Map<String,Object> variables2 = runtimeService.getVariables(processInstanceId);
                if(variables2!=null){
                    Object assignee = variables2.get("assignee");
                    if(assignee!=null){
                        JSONObject o = JSONObject.parseObject(assignee.toString());
                        Object o1 =  o.get("assignUser");
                        Object o2 =  o.get("assignNode");
                        if(o1!=null && o2!=null){
                            if(o2.toString().equals(t.getTaskDefinitionKey())){// 指定的节点
                                t.setAssignee(o1.toString());// 指定人
                            }
                        }

                    }
                }
            }


        }else {//会签，不做处理，理论上，会签不会指定人

        }
        return "完成任务成功";
    }

    @Override
    /** 退回流程[审核不通过]*/
    public String revertTask(Map<String,String> param) {
        String processInstanceId = param.get("processInstanceId");
        String taskId = param.get("taskId");//当前任务Id
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if(task==null) {
            return "流程未启动或已执行完成，无法撤回" ;
        }
        String userId =  param.get("userId");//操作人Id
        String assignee =  param.get("assignee");// 待处理人
        String nowNode =   param.get("nowNode");//当前节点
        String targetNode =   param.get("targetNode");//退回的节点
        // 判断两个节点 是否属于流程的 , 并且 nowNode 和 targetNode 不能相同
        String approveOpinion =   param.get("approveOpinion");//审核意见
        String opinion = userId+ ":退回了流程："+approveOpinion;
        taskService.addComment(taskId,processInstanceId,opinion);
        managementService.executeCommand(new MoveNodeCmd(taskId,targetNode,opinion,false));
        // 驳回之后的操作
        List<Task> tasks  = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        if(tasks == null || tasks.size()==0){//流程归档了
            return "success";
        }else{//有任务了，可以设置处理人 等
            for(Task t : tasks){
                t.setAssignee("assignee");
            }
        }
        return "success";
    }


    /** 挂起一个流程 */
    public String stopflow(String processInstanceId){
        ProcessInstance p = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if(!p.isSuspended()){//判断流程是否挂起
            runtimeService.suspendProcessInstanceById(processInstanceId);
            // repositoryService.suspendProcessDefinitionById(p.getProcessDefinitionId());//挂起所有流程部署，不能发起流程
        }
        return "挂起流程成功";
    }
    /** 唤醒挂起的流程 */
    public String reStartflow(String processInstanceId){
        ProcessInstance p = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if(p.isSuspended()){//判断流程是否挂起
            runtimeService.activateProcessInstanceById(processInstanceId);
            // repositoryService.activateProcessDefinitionById(p.getProcessDefinitionId());//挂起所有流程部署，不能发起流程
        }
        return "重启流程成功";
    }


    /**  获取 用户已经完成的任务  */
    public List<Map<String,Object>> getFlowComplete(String userId){
        return null;
    }



    /**获取流程图*/
    public void getGraphics(String processInstanceId,HttpServletResponse response){
       try {
           InputStream inputStream = getDiagram(processInstanceId);
           if(inputStream==null){
               return;
           }
           response.setContentType("image/png");
           BufferedImage image = ImageIO.read(inputStream);
           OutputStream out = response.getOutputStream();
           ImageIO.write(image,"png",out);
           inputStream.close();
           out.close();
       }catch (Exception e){
           e.printStackTrace();
       }

    }
    public InputStream getDiagram(String processInstanceId){
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        String processDefinitionId = "";
        if(processInstance == null ){ // 流程已经结束
            HistoricProcessInstance historicProcessInstance =historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            if(historicProcessInstance == null){//没有结束的流程
                return null;
            }else{
                processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            }
        }else{
            processDefinitionId = processInstance.getProcessDefinitionId();
        }
        String fontName = "宋体";
        BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        List<String> currentActs = new ArrayList<>();
        if(processInstance != null){
            currentActs = runtimeService.getActiveActivityIds(processInstanceId);
        }
        return  processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator()
                .generateDiagram(model,"png",currentActs,new ArrayList<String>(),fontName,fontName,fontName,null,1.0);
    }


    /**
     * 删除所有流程信息【慎用】
     */
    public void clearAllData(HttpServletResponse response) {
	/*
		Task task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
		TaskService service1=engine.getTaskService();
		RuntimeService service2 = engine.getRuntimeService();
        List list1 = service.createModelQuery().list();
        List list2 = service.createProcessDefinitionQuery().list();//获取流程
        List list3 = service.createDeploymentQuery().list();//部署信息
        RuntimeService service2 = engine.getRuntimeService();
        List list01 = service2.createProcessInstanceQuery().list();//启动流程后的关联表
        List list02 = service2.createExecutionQuery().list();
		*/
        //后期 可以把 这个 删除 语句 放到 存储 过程去,直接调用 一个sql就行了
        String tables[] = {
                "act_evt_log","act_ge_property", "act_procdef_info",
                "act_ge_bytearray","act_hi_actinst","act_hi_comment",
                "act_hi_detail","act_hi_identitylink","act_hi_procinst",
                "act_hi_taskinst","act_hi_varinst","act_re_deployment",
                "act_re_model","act_ru_identitylink","act_ru_event_subscr",
                 "act_ru_variable","act_ru_job",
                "act_ru_deadletter_job","act_ru_integration","act_ru_suspended_job","act_ru_timer_job",
                "act_ru_task","act_ru_execution","act_re_procdef",
                "act_hi_attachment"
        };
        for(String table : tables){
            String sql ="DELETE FROM " + table ;
           // utilService.executeSql(sql);
        }
    }

}
